// Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
// All rights reserved.
// Code licensed under the BSD License.
// http://www.anki3d.org/LICENSE

#pragma once

#include <AnKi/Resource/Common.h>
#include <AnKi/Gr/ShaderProgram.h>
#include <AnKi/Util/HashMap.h>
#include <AnKi/Util/StringList.h>
#include <AnKi/ShaderCompiler/ShaderBinary.h>

namespace anki {

/// @addtogroup resource
/// @{

enum class RayTracingShaderGroupType : U8
{
	kRayGen,
	kMiss,
	kHit,

	kCount,
	kFirst = 0
};

/// This is a ray tracing library. Essentially a shader program with some functionality on how to get group indices.
class ShaderProgramRaytracingLibrary
{
	friend class ShaderProgramResourceSystem;

public:
	CString getLibraryName() const
	{
		return m_libraryName;
	}

	U32 getRayTypeCount() const
	{
		ANKI_ASSERT(m_rayTypeCount < kMaxU32);
		return m_rayTypeCount;
	}

	const ShaderProgramPtr& getShaderProgram() const
	{
		return m_program;
	}

	/// Given the filename of a program (that contains ray tracing shaders) and a specific mutation get the shader handle index.
	U32 getShaderGroupHandleIndex(CString resourceFilename, U64 mutationHash, RayTracingShaderGroupType groupType) const
	{
		return getIndex(generateShaderGroupGroupHash(resourceFilename, mutationHash, groupType));
	}

private:
	ResourceString m_libraryName;
	U32 m_rayTypeCount = kMaxU32;
	ShaderProgramPtr m_program;
	ResourceHashMap<U64, U32> m_resourceHashToShaderGroupHandleIndex;

	/// Given the filename of a program (that contains ray tracing shaders) and a specific mutation get a hash back.
	static U64 generateShaderGroupGroupHash(CString resourceFilename, U64 mutationHash, RayTracingShaderGroupType groupType);

	/// The hash generated by generateShaderGroupGroupHash() can be used to retrieve the group position in the m_program.
	U32 getIndex(U64 groupHash) const
	{
		auto it = m_resourceHashToShaderGroupHandleIndex.find(groupHash);
		ANKI_ASSERT(it != m_resourceHashToShaderGroupHandleIndex.getEnd());
		return *it;
	}
};

/// A system that does some work on shader programs before resources start loading.
class ShaderProgramResourceSystem
{
public:
	ShaderProgramResourceSystem()
	{
	}

	~ShaderProgramResourceSystem() = default;

	Error init();

	ConstWeakArray<ShaderProgramRaytracingLibrary> getRayTracingLibraries() const
	{
		return m_rtLibraries;
	}

private:
	class ShaderH;
	class ShaderGroup;
	class Lib;

	ResourceDynamicArray<ShaderProgramRaytracingLibrary> m_rtLibraries;

	static Error createRayTracingPrograms(ResourceDynamicArray<ShaderProgramRaytracingLibrary>& outLibs);
};
/// @}

} // end namespace anki
